<div id="app"></div>
<script>
function isObject(obj) {
    return (typeof obj === "object" && obj !== null) 
}

function reactive(obj) {
    if (!isObject(obj)){
        return obj
    }

    return new Proxy(obj, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver)
            console.log("get", key)
            track(target, key)
            return isObject(res) ? reactive(res) : res
        },
        set(target, key, val, receiver) {
            const res = Reflect.set(target, key, val, receiver)
            trigger(target, key)
            console.log('set', key)
            return res
        },
        deleteProperty(target, key) {
            const res = Reflect.deleteProperty(target, key)
            trigger(target, key)
            console.log("delete property")
            return res
        }
    })
}

const effectStack = []
function createReactiveEffect(fn) {
    const effect = function (...args) {
        try {
            effectStack.push(effect)
            return fn(...args)
        } finally {
            effectStack.pop()
        }
    }
    return effect
}

function effect(fn) {
    const e = createReactiveEffect(fn)
    e()
    return e
}

const targetMap = new WeakMap()
function track(target, key) {
    const effect = effectStack[effectStack.length - 1]
    if (effect) {
        let depMap = targetMap.get(target)
        if (!depMap) {
            depMap = new Map()
            targetMap.set(target, depMap)
        }

        let deps = depMap.get(key)

        if (!deps) {
            deps = new Set()
            depMap.set(key, deps)
        }

        deps.add(effect)
    }
}

function trigger(target, key) {
    const depMap = targetMap.get(target)
    if (!depMap) {
        return
    }
    const deps = depMap.get(key)
    if (deps) {
        deps.forEach(dep => dep());
    }
}

const state = reactive({
    name: 'kkb',
    age: 8 ,
    anhao: "you can you up"
})

effect(() => {
    app.innerHTML = `
      <h1>${state.name} is ${state.age} years</h1>
      <h2>AnHao: ${state.anhao}</h2>
    `
    console.log('effect', state.foo);
})
</script>
